package com.zpp.friend.service.user.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zpp.common.core.constants.CacheConstants;
import com.zpp.common.core.constants.Constants;
import com.zpp.common.core.constants.HttpConstants;
import com.zpp.common.core.domain.LoginUser;
import com.zpp.common.core.domain.Result;
import com.zpp.common.core.domain.vo.LoginUserVO;
import com.zpp.common.core.enums.ResultCode;
import com.zpp.common.core.enums.UserIdentity;
import com.zpp.common.core.utils.ThreadLocalUtil;
import com.zpp.common.message.service.MailService;
import com.zpp.common.redis.service.RedisService;
import com.zpp.common.security.exception.ServiceException;
import com.zpp.common.security.service.TokenService;
import com.zpp.friend.domain.user.User;
import com.zpp.friend.domain.user.dto.UserUpdateDTO;
import com.zpp.friend.domain.user.vo.UserVO;
import com.zpp.friend.manager.UserCacheManager;
import com.zpp.friend.mapper.user.UserMapper;
import com.zpp.friend.service.user.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MailService mailService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UserCacheManager userCacheManager;

    @Value("${jwt.secret}")
    private String secret;

    @Value("${switch.is-send}")
    private boolean isSend;  //打开true 关闭false

    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;

    @Override
    public boolean sendCode(String email) {
        //1.验证邮箱规范  已添加接受参数校验注解，无需再校验了
/*        if(!checkEmail(email)) {
            throw new ServiceException(ResultCode.FAILED_USER_EMAIL);
        }*/

        //2.生成key
        //生成存储用户验证码key
        String codeKey = getEmailCodeKey(email);
        //生成存储获取验证码次数key
        String countKey = getCodeCountKey(email);

        //3.检查时间与次数
        //检查是否在一分钟之内已经获取过验证吗了
        Long expire = redisService.getExpire(codeKey, TimeUnit.SECONDS);
        if(expire != null && (CacheConstants.CODE_EXPIRE_TIME - expire < 60)) {
            //一分钟内已经获取过了，则不再发送验证码
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }
        //检查获取次数已达今日上限
        Long count = redisService.getCacheObject(countKey, Long.class);
        if(count != null && count >= CacheConstants.CODE_MAX_COUNT){
            throw new ServiceException(ResultCode.FAILED_COUNT_LIMIT);
        }

        //4.往redis中更新数据
        //生成验证码
        String code = isSend ? RandomUtil.randomNumbers(6) : Constants.DEFAULT_CODE;
        //将验证码信息保存到redis中，并设置过期时间
        redisService.setCacheObject(codeKey, code, CacheConstants.CODE_EXPIRE_TIME, TimeUnit.SECONDS);
        //次数自增一次，如果当前countKey不存在，redis会自动创建
        redisService.increment(countKey);

        //说明是当天第一次操作,设置过期时间
        if(count == null) {
            //计算当前时间到第二天零点（00:00:00）的秒数
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(countKey, seconds, TimeUnit.SECONDS);
        }

        //5.发送邮件
        if(isSend) {
            try {
                //通过邮件发送验证码给用户
                mailService.send(email, code);
            } catch (Exception e) {
                throw new ServiceException(ResultCode.FAILED_SEND_EMAIL);
            }
        }

        return true;
    }


    @Override
    public String login(String email, String code) {
        //检查输入验证码是否正确
        checkCode(email, code);

        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getEmail, email));
        //1.用户不存在先注册
        if(user == null) {
            User register = new User();
            register.setEmail(email);
            register.setNickName("用户" + RandomUtil.randomNumbers(10)); //新用户随机生成一个用户名
            userMapper.insert(register);
            user = register;
        }

        //验证完成，返回token信息
        return tokenService.createToken(user.getUserId(), secret, UserIdentity.ORDINARY,user.getNickName(),user.getHeadImage());
    }

    //检查验证码是否正确
    public void checkCode(String email, String code){
        //获取redis中的验证码
        String emailCodeKey = getEmailCodeKey(email);
        String cacheCode = redisService.getCacheObject(emailCodeKey, String.class);
        //验证码已失效
        if(cacheCode == null){
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        //校验验证码
        if(!cacheCode.equals(code)){
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }
        //校验成功，删除验证码
        redisService.deleteObject(emailCodeKey);
    }


    @Override
    public boolean logout(String token) {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteToken(token, secret);
    }

    @Override
    public Result<LoginUserVO> info(String token) {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }

        LoginUser loginUser = tokenService.getInfo(token, secret);
        //获取失败，不存在登录对象
        if(loginUser == null) {
            return Result.fail();
        }
        //获取成功，创建VO对象进行返回
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if(StrUtil.isNotEmpty(loginUser.getHeadImage())) {
            loginUserVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        }
        return Result.success(loginUserVO);
    }

    //验证邮件合法性
    public boolean checkEmail(String email) {
        Pattern regex = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
        Matcher matcher = regex.matcher(email);
        return matcher.matches();
    }

    @Override
    public UserVO detail() {
        //从当前线程中获取userId
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if(userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }

         UserVO userVO= userCacheManager.getUserById(userId);
        if(userVO == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if(StrUtil.isNotEmpty(userVO.getHeadImage())) {
            userVO.setHeadImage(downloadUrl + userVO.getHeadImage());
        }
        return userVO;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        //从线程中拿到当前用户id
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if(userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //根据id查询数据库中对应的用户对象
        User user = userMapper.selectById(userId);
        if(user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }

        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        user.setEmail(userUpdateDTO.getEmail());

        //更新用户缓存
        userCacheManager.refreshUser(user);

        //刷新登录token信息
        tokenService.refreshLoginUser(user.getNickName(), user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));

        //更新数据库中的数据
        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        //从线程中拿到当前用户id
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if(userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //根据id查询数据库中对应的用户对象
        User user = userMapper.selectById(userId);
        if(user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setHeadImage(headImage);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        //刷新登录token信息
        tokenService.refreshLoginUser(user.getNickName(), user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        //更新数据库中的数据
        return userMapper.updateById(user);

    }

    public String getEmailCodeKey(String email) {
        return CacheConstants.EMAIL_CODE_KEY + email;
    }

    private String getCodeCountKey(String email) {
        return CacheConstants.CODE_COUNT_KEY+email;
    }


}
